---
title: "Classes and Structures - OOP, Properties, Methods"
description: "Learn Swift classes and structures: OOP basics, properties, methods, constructors, and comparison with JavaScript"
---

# Classes and Structures: OOP, Properties, Methods

In this module, we explore object-oriented programming (OOP) in Swift and compare it with JavaScript's class and prototype system. Swift provides both classes (reference types) and structures (value types), each with their own use cases and features.

## Table of Contents
- [Introduction: Classes vs Structures](#introduction-classes-vs-structures)
- [Declaring Classes and Structures](#declaring-classes-and-structures)
- [Properties](#properties)
- [Methods](#methods)
- [Initializers (Constructors)](#initializers-constructors)
- [Inheritance and Polymorphism](#inheritance-and-polymorphism)
- [Value Types vs Reference Types](#value-types-vs-reference-types)
- [Protocols (Interfaces)](#protocols-interfaces)
- [Advanced Features](#advanced-features)
- [Exercises](#exercises)
- [Key Takeaways](#key-takeaways)

## Introduction: Classes vs Structures

- **Class**: Reference type, supports inheritance, deinitializers, identity comparison, reference semantics.
- **Struct**: Value type, no inheritance, copied on assignment, value semantics, lightweight and preferred for simple data models.

| Feature         | JavaScript Class | Swift Class | Swift Struct |
|-----------------|------------------|-------------|--------------|
| Reference Type  | Yes              | Yes         | No           |
| Value Type      | No               | No          | Yes          |
| Inheritance     | Yes              | Yes         | No           |
| Deinitializer   | No               | Yes         | No           |
| Mutability      | Dynamic          | Controlled  | Controlled   |
| Protocols       | No (interface)   | Yes         | Yes          |
| Use Case        | All OOP          | OOP         | Data models, lightweight types |

## Declaring Classes and Structures

<UniversalEditor title="Class and Struct Declaration Comparison" compare={true}>
```javascript !! js
// JavaScript class declaration
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    greet() {
        console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    }
}

const alice = new Person("Alice", 30);
alice.greet(); // Hello, my name is Alice and I am 30 years old.

// JavaScript object literal (struct-like)
const point = { x: 3, y: 4 };
console.log(point.x, point.y);
```

```swift !! swift
// Swift class declaration
class Person {
    var name: String
    var age: Int

    // Initializer
    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }

    // Method
    func greet() {
        print("Hello, my name is \(name) and I am \(age) years old.")
    }
}

let alice = Person(name: "Alice", age: 30)
alice.greet() // Hello, my name is Alice and I am 30 years old.

// Swift struct declaration
struct Point {
    var x: Int
    var y: Int
}

let point = Point(x: 3, y: 4)
print(point.x, point.y)
```
</UniversalEditor>

## Properties

Swift supports stored properties, computed properties, and property observers. JavaScript properties are always dynamic.

<UniversalEditor title="Properties Comparison" compare={true}>
```javascript !! js
// JavaScript properties
class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }

    // Computed property (getter)
    get area() {
        return this.width * this.height;
    }

    // Property observer (manual)
    set width(value) {
        this._width = value;
        console.log("Width changed to", value);
    }
    get width() {
        return this._width;
    }
}

const rect = new Rectangle(10, 5);
console.log(rect.area); // 50
rect.width = 20; // Width changed to 20
```

```swift !! swift
// Swift properties
struct Rectangle {
    var width: Double {
        didSet {
            print("Width changed to", width)
        }
    }
    var height: Double

    // Computed property
    var area: Double {
        return width * height
    }
}

var rect = Rectangle(width: 10, height: 5)
print(rect.area) // 50
rect.width = 20 // Width changed to 20
```
</UniversalEditor>

## Methods

Both JavaScript and Swift support instance methods. Swift also supports static/class methods and mutating methods for structs.

<UniversalEditor title="Methods Comparison" compare={true}>
```javascript !! js
// JavaScript instance and static methods
class Counter {
    constructor() {
        this.count = 0;
    }
    increment() {
        this.count++;
    }
    static description() {
        return "A simple counter";
    }
}

const counter = new Counter();
counter.increment();
console.log(counter.count); // 1
console.log(Counter.description()); // A simple counter
```

```swift !! swift
// Swift instance, static, and mutating methods
struct Counter {
    var count = 0
    // Mutating method (for structs)
    mutating func increment() {
        count += 1
    }
    // Static method
    static func description() -> String {
        return "A simple counter"
    }
}

var counter = Counter()
counter.increment()
print(counter.count) // 1
print(Counter.description()) // A simple counter
```
</UniversalEditor>

## Initializers (Constructors)

Swift uses `init` for initializers. Structs get a memberwise initializer by default.

<UniversalEditor title="Initializers Comparison" compare={true}>
```javascript !! js
// JavaScript constructor
class User {
    constructor(username, email) {
        this.username = username;
        this.email = email;
    }
}
const user = new User("bob", "bob@example.com");

// Object literal
const config = { host: "localhost", port: 8080 };
```

```swift !! swift
// Swift class initializer
class User {
    var username: String
    var email: String
    init(username: String, email: String) {
        self.username = username
        self.email = email
    }
}
let user = User(username: "bob", email: "bob@example.com")

// Swift struct memberwise initializer
struct Config {
    var host: String
    var port: Int
}
let config = Config(host: "localhost", port: 8080)
```
</UniversalEditor>

## Inheritance and Polymorphism

Swift classes support single inheritance, while JavaScript classes also support single inheritance but can use mixins and composition.

### Basic Inheritance

<UniversalEditor title="Inheritance Comparison" compare={true}>
```javascript !! js
// JavaScript inheritance
class Animal {
    constructor(name) {
        this.name = name;
    }
    speak() {
        console.log(`${this.name} makes a sound`);
    }
}

class Dog extends Animal {
    constructor(name, breed) {
        super(name);
        this.breed = breed;
    }
    speak() {
        console.log(`${this.name} barks`);
    }
}

const dog = new Dog("Buddy", "Golden Retriever");
dog.speak(); // Buddy barks
```

```swift !! swift
// Swift inheritance
class Animal {
    var name: String
    
    init(name: String) {
        self.name = name
    }
    
    func speak() {
        print("\(name) makes a sound")
    }
}

class Dog: Animal {
    var breed: String
    
    init(name: String, breed: String) {
        self.breed = breed
        super.init(name: name)
    }
    
    override func speak() {
        print("\(name) barks")
    }
}

let dog = Dog(name: "Buddy", breed: "Golden Retriever")
dog.speak() // Buddy barks
```
</UniversalEditor>

### Polymorphism

<UniversalEditor title="Polymorphism Comparison" compare={true}>
```javascript !! js
// JavaScript polymorphism
class Cat extends Animal {
    speak() {
        console.log(`${this.name} meows`);
    }
}

function makeAnimalSpeak(animal) {
    animal.speak();
}

const animals = [
    new Dog("Buddy", "Golden"),
    new Cat("Whiskers")
];

animals.forEach(makeAnimalSpeak);
// Buddy barks
// Whiskers meows
```

```swift !! swift
// Swift polymorphism
class Cat: Animal {
    override func speak() {
        print("\(name) meows")
    }
}

func makeAnimalSpeak(_ animal: Animal) {
    animal.speak()
}

let animals: [Animal] = [
    Dog(name: "Buddy", breed: "Golden"),
    Cat(name: "Whiskers")
]

for animal in animals {
    makeAnimalSpeak(animal)
}
// Buddy barks
// Whiskers meows
```
</UniversalEditor>

## Value Types vs Reference Types

This is a fundamental difference between Swift and JavaScript.

### Reference Types (Classes)

<UniversalEditor title="Reference Types Comparison" compare={true}>
```javascript !! js
// JavaScript: All objects are reference types
class Person {
    constructor(name) {
        this.name = name;
    }
}

const person1 = new Person("Alice");
const person2 = person1; // Reference copy
person2.name = "Bob";
console.log(person1.name); // "Bob" (shared reference)
console.log(person2.name); // "Bob"
console.log(person1 === person2); // true (same reference)
```

```swift !! swift
// Swift: Classes are reference types
class Person {
    var name: String
    
    init(name: String) {
        self.name = name
    }
}

let person1 = Person(name: "Alice")
let person2 = person1 // Reference copy
person2.name = "Bob"
print(person1.name) // "Bob" (shared reference)
print(person2.name) // "Bob"
print(person1 === person2) // true (same reference)
```
</UniversalEditor>

### Value Types (Structs)

<UniversalEditor title="Value Types Comparison" compare={true}>
```javascript !! js
// JavaScript: No true value types, but can simulate with Object.assign
const point1 = { x: 1, y: 2 };
const point2 = Object.assign({}, point1); // Shallow copy
point2.x = 3;
console.log(point1.x); // 1 (original unchanged)
console.log(point2.x); // 3
console.log(point1 === point2); // false (different objects)
```

```swift !! swift
// Swift: Structs are value types
struct Point {
    var x: Int
    var y: Int
}

var point1 = Point(x: 1, y: 2)
var point2 = point1 // Value copy
point2.x = 3
print(point1.x) // 1 (original unchanged)
print(point2.x) // 3
print(point1 == point2) // false (different values)
```
</UniversalEditor>

## Protocols (Interfaces)

Swift protocols are similar to JavaScript interfaces but more powerful.

<UniversalEditor title="Protocols Comparison" compare={true}>
```javascript !! js
// JavaScript: No built-in interfaces, but can use duck typing
class Drawable {
    draw() {
        throw new Error("draw() must be implemented");
    }
}

class Circle {
    constructor(radius) {
        this.radius = radius;
    }
    draw() {
        console.log(`Drawing circle with radius ${this.radius}`);
    }
}

class Square {
    constructor(side) {
        this.side = side;
    }
    draw() {
        console.log(`Drawing square with side ${this.side}`);
    }
}

function drawShape(shape) {
    if (typeof shape.draw === 'function') {
        shape.draw();
    }
}

drawShape(new Circle(5));
drawShape(new Square(4));
```

```swift !! swift
// Swift: Protocols with explicit conformance
protocol Drawable {
    func draw()
}

class Circle: Drawable {
    var radius: Double
    
    init(radius: Double) {
        self.radius = radius
    }
    
    func draw() {
        print("Drawing circle with radius \(radius)")
    }
}

struct Square: Drawable {
    var side: Double
    
    func draw() {
        print("Drawing square with side \(side)")
    }
}

func drawShape(_ shape: Drawable) {
    shape.draw()
}

drawShape(Circle(radius: 5))
drawShape(Square(side: 4))
```
</UniversalEditor>

## Advanced Features

### Property Wrappers and Computed Properties

<UniversalEditor title="Advanced Properties" compare={true}>
```javascript !! js
// JavaScript: Getters and setters
class BankAccount {
    constructor(balance) {
        this._balance = balance;
    }
    
    get balance() {
        return this._balance;
    }
    
    set balance(value) {
        if (value < 0) {
            throw new Error("Balance cannot be negative");
        }
        this._balance = value;
    }
}

const account = new BankAccount(1000);
console.log(account.balance); // 1000
account.balance = 1500; // OK
// account.balance = -100; // Error
```

```swift !! swift
// Swift: Computed properties with property observers
class BankAccount {
    private var _balance: Double = 0
    
    var balance: Double {
        get {
            return _balance
        }
        set {
            if newValue < 0 {
                fatalError("Balance cannot be negative")
            }
            _balance = newValue
        }
    }
    
    // Property observer
    var accountNumber: String = "" {
        didSet {
            print("Account number changed from \(oldValue) to \(accountNumber)")
        }
    }
}

let account = BankAccount()
account.balance = 1000
print(account.balance) // 1000
account.balance = 1500 // OK
// account.balance = -100 // Fatal error
```
</UniversalEditor>

### Static and Class Methods

<UniversalEditor title="Static and Class Methods" compare={true}>
```javascript !! js
// JavaScript: Static methods
class MathUtils {
    static add(a, b) {
        return a + b;
    }
    
    static multiply(a, b) {
        return a * b;
    }
    
    static PI = 3.14159;
}

console.log(MathUtils.add(5, 3)); // 8
console.log(MathUtils.PI); // 3.14159
```

```swift !! swift
// Swift: Static and class methods
struct MathUtils {
    static func add(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
    
    static func multiply(_ a: Int, _ b: Int) -> Int {
        return a * b
    }
    
    static let PI = 3.14159
}

class Calculator {
    class func add(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
    
    static func multiply(_ a: Int, _ b: Int) -> Int {
        return a * b
    }
}

print(MathUtils.add(5, 3)) // 8
print(MathUtils.PI) // 3.14159
print(Calculator.add(5, 3)) // 8
```
</UniversalEditor>

## Exercises

### Exercise 1: Create a Student Management System

<UniversalEditor title="Exercise 1: Student Management" compare={true}>
```javascript !! js
// JavaScript solution
class Student {
    constructor(name, id, grades = []) {
        this.name = name;
        this.id = id;
        this.grades = grades;
    }
    
    addGrade(grade) {
        if (grade >= 0 && grade <= 100) {
            this.grades.push(grade);
        }
    }
    
    get average() {
        if (this.grades.length === 0) return 0;
        return this.grades.reduce((sum, grade) => sum + grade, 0) / this.grades.length;
    }
    
    get letterGrade() {
        const avg = this.average;
        if (avg >= 90) return 'A';
        if (avg >= 80) return 'B';
        if (avg >= 70) return 'C';
        if (avg >= 60) return 'D';
        return 'F';
    }
}

const student = new Student("Alice", "12345");
student.addGrade(85);
student.addGrade(92);
student.addGrade(78);
console.log(`${student.name}: ${student.average.toFixed(1)} (${student.letterGrade})`);
```

```swift !! swift
// Swift solution
struct Student {
    let name: String
    let id: String
    private var grades: [Double] = []
    
    init(name: String, id: String) {
        self.name = name
        self.id = id
    }
    
    mutating func addGrade(_ grade: Double) {
        if grade >= 0 && grade <= 100 {
            grades.append(grade)
        }
    }
    
    var average: Double {
        if grades.isEmpty { return 0 }
        return grades.reduce(0, +) / Double(grades.count)
    }
    
    var letterGrade: String {
        let avg = average
        switch avg {
        case 90...:
            return "A"
        case 80..<90:
            return "B"
        case 70..<80:
            return "C"
        case 60..<70:
            return "D"
        default:
            return "F"
        }
    }
}

var student = Student(name: "Alice", id: "12345")
student.addGrade(85)
student.addGrade(92)
student.addGrade(78)
print("\(student.name): \(String(format: "%.1f", student.average)) (\(student.letterGrade))")
```
</UniversalEditor>

### Exercise 2: Implement a Shape Hierarchy

<UniversalEditor title="Exercise 2: Shape Hierarchy" compare={true}>
```javascript !! js
// JavaScript solution
class Shape {
    constructor(color) {
        this.color = color;
    }
    
    area() {
        throw new Error("area() must be implemented");
    }
    
    describe() {
        console.log(`A ${this.color} shape with area ${this.area()}`);
    }
}

class Circle extends Shape {
    constructor(color, radius) {
        super(color);
        this.radius = radius;
    }
    
    area() {
        return Math.PI * this.radius * this.radius;
    }
}

class Rectangle extends Shape {
    constructor(color, width, height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    
    area() {
        return this.width * this.height;
    }
}

const shapes = [
    new Circle("red", 5),
    new Rectangle("blue", 4, 6)
];

shapes.forEach(shape => shape.describe());
```

```swift !! swift
// Swift solution
class Shape {
    var color: String
    
    init(color: String) {
        self.color = color
    }
    
    func area() -> Double {
        fatalError("area() must be implemented")
    }
    
    func describe() {
        print("A \(color) shape with area \(area())")
    }
}

class Circle: Shape {
    var radius: Double
    
    init(color: String, radius: Double) {
        self.radius = radius
        super.init(color: color)
    }
    
    override func area() -> Double {
        return Double.pi * radius * radius
    }
}

class Rectangle: Shape {
    var width: Double
    var height: Double
    
    init(color: String, width: Double, height: Double) {
        self.width = width
        self.height = height
        super.init(color: color)
    }
    
    override func area() -> Double {
        return width * height
    }
}

let shapes: [Shape] = [
    Circle(color: "red", radius: 5),
    Rectangle(color: "blue", width: 4, height: 6)
]

for shape in shapes {
    shape.describe()
}
```
</UniversalEditor>

## Key Takeaways

### Swift OOP Advantages
1. **Value Types**: Structs provide value semantics, preventing unexpected mutations
2. **Type Safety**: Strong typing prevents runtime errors
3. **Protocols**: Flexible interface system for both classes and structs
4. **Property Observers**: Built-in support for property change notifications
5. **Memory Management**: ARC handles memory automatically

### Key Differences from JavaScript
1. **Value vs Reference**: Swift distinguishes between value and reference types
2. **Inheritance**: Only classes support inheritance in Swift
3. **Mutability**: Swift structs require explicit mutating methods
4. **Protocols**: Swift has formal protocol system vs JavaScript duck typing
5. **Memory Management**: ARC vs garbage collection

### Best Practices
1. **Use structs for simple data models** and value semantics
2. **Use classes for complex objects** that need inheritance
3. **Leverage protocols** for flexible interfaces
4. **Prefer composition over inheritance** when possible
5. **Use property observers** for reactive programming
6. **Consider value types** for thread safety and performance

### Next Steps
In the next module, we'll explore protocols and extensions in Swift, including protocol-oriented programming, protocol extensions, and how they compare to JavaScript's approach to interfaces and mixins. 